1 /*
2 * Copyright 1994-2012 The MathWorks, Inc.
3 *
4 * File: rtiostream_tcpip.c
5 *
6 * Abstract: This source file implements both client-side and server-side TCP/IP
7 * and UDP/IP communication. Typically, this driver is used to support host-target
8 * communication where the client-side device driver runs on the host and the
9 * server-side driver runs on the target. For this implementation, both
10 * client-side and server-side driver code has been combined into a single
11 * file.
12 *
13 * If you are using this code as a starting point to implement a TCP/IP or
14 * UDP/IP driver for a custom target it is only necessary to include code
15 * for the server side of the connection.
16 */
17
18 #ifndef _WIN32
19 /* Required BSD Unix extensions are not available by default on certain Unix
20 * distributions */
21 #define _BSD_SOURCE
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include <limits.h>
29 #include "rtiostream.h"
30 #include "tmwtypes.h"
31
32 #ifdef _WIN32
33 /* WINDOWS */
34
35 #if defined(_MSC_VER)
36 /* temporarily disable warning triggered
37 * by windows.h */
38 #pragma warning(push)
39 #pragma warning(disable: 4255)
40 #endif
41
42 #include <windows.h>
43
44 #if defined(_MSC_VER)
45 /* restore warning */
46 #pragma warning(pop)
47 #endif
48
49 # ifdef __LCC__
50 # ifndef __LCC64__
51 # include <winsock2.h>
52 # endif
53 # include <errno.h>
54 # endif
55
56 #define RTIOSTREAM_ECONNRESET WSAECONNRESET
57
58 #elif defined(VXWORKS)
59 /*VxWorks headers*/
60 # include <selectLib.h>
61 # include <sockLib.h>
62 # include <inetLib.h>
63 # include <ioLib.h>
64 # include <taskLib.h>
65 # include <netinet/tcp.h>
66
67 #define RTIOSTREAM_ECONNRESET ECONNRESET
68
69 #else
70 /* UNIX */
71 # include <signal.h>
72 # include <sys/time.h> /* Linux */
73 # include <sys/types.h> /* Linux */
74 # include <sys/socket.h>
75 # include <netinet/in.h> /* Linux */
76 # include <netinet/tcp.h> /* Linux */
77 # include <arpa/inet.h> /* Linux */
78 # include <netdb.h>
79 # include <errno.h>
80 # include <fcntl.h>
81 # include <unistd.h>
82
83 #define RTIOSTREAM_ECONNRESET ECONNRESET
84 #endif
85
86 #ifdef USE_MEXPRINTF
87 #include "mex.h"
88 #define printf mexPrintf
89 #define SERVER_PORT_PRINTF(FORMAT, ARG1) mexPrintf(FORMAT, ARG1)
90 #else
91 /* If stdout is redirected to file, it is essential that the port number is
92 * available immediately in the output file. With LCC, printf does not flush
93 * correctly to the redirected output file - use fprintf & fflush instead. */
94 #define SERVER_PORT_PRINTF(FORMAT, ARG1) fprintf(stdout, FORMAT, ARG1); \
95 fflush(stdout)
96 #endif
97
98 /***************** DEFINES ****************************************************/
99
100 #define HOSTNAME_MAXLEN (64U)
101
102 #define SERVER_PORT_NUM (17725U) /* sqrt(pi)*10000 */
103
104 /*
105 * EXT_BLOCKING
106 *
107 * Depending on the implementation of the main program (e.g., grt_main.c,
108 * rt_main.c), the EXT_BLOCKING flag must be set to either 0 or 1.
109 *
110 * rt_main.c (tornado/vxworks): rt_main.c is a real-time, multi-tasking target.
111 * The upload and packet servers are called via background (low priority) tasks.
112 * In this case, it is o.k. for the transport function to block as the blocked
113 * tasks will simply be pre-empted in order to enable the model to run. It is
114 * desirable to block instead of to poll to free up the cpu for any other
115 * potential work.
116 */
117 #ifdef VXWORKS
118 # define EXT_BLOCKING (1)
119 #else
120 # define EXT_BLOCKING (0)
121 #endif
122
123 /* timeout of 0 means to return immediately */
124 #define BLOCKING_RECV_TIMEOUT_NOWAIT (0)
125 /* timeout of -1 means to wait indefinitely */
126 #define BLOCKING_RECV_TIMEOUT_NEVER (-1)
127 /* rogue value for blocking receive timeout */
128 #define DEFAULT_BLOCKING_RECV_TIMEOUT (-2)
129 /* timeout of -3 means to wait for 10 ms to avoid high CPU load */
130 #define BLOCKING_RECV_TIMEOUT_10MS (-3)
131 /* wake up from blocking every second */
132 #define DEFAULT_BLOCKING_RECV_TIMEOUT_SECS_CLIENT (1)
133 /* only wake up from blocking when data arrives */
134 #define DEFAULT_BLOCKING_RECV_TIMEOUT_SECS_SERVER (BLOCKING_RECV_TIMEOUT_NEVER)
135 /* server wait time for client to close its socket */
136 #define BLOCKING_RECV_TIMEOUT_SOCK_SHUTDOWN (15)
137
138 /* default isVerbose value */
139 #define DEFAULT_IS_VERBOSE 0
140
141 /* default protocol value */
142 #define DEFAULT_PROTOCOL TCP_PROTOCOL
143 /* allowed -protocol strings */
144 #define TCP_PROTOCOL_STRING "TCP"
145 #define UDP_PROTOCOL_STRING "UDP"
146 #define UDP_PACKET_LOSS_DETECTON_PROTOCOL_STRING "UDP_PACKET_LOSS_DETECTION"
147
148 /* default UDP max packet size
149 *
150 * The maximum size of UDP packets that are transmitted / received must be
151 * consistent on the host and target otherwise receive errors will occur at
152 * the side with the smaller buffer size specified.
153 *
154 * Use the "-maxudppacketsize SIZE" argument to specify a different packet size.
155 * This option is particularly useful when using a custom server implementation
156 * that uses a different max packet size to the default.
157 *
158 * The maximum UDP payload is 65507 bytes, which can be achieved for localhost
159 * based communications on Linux and Windows, but Mac has a lower size of
160 * 9216.
161 */
162 #define UDP_MAX_PACKET_SIZE 9216
163 #define DEFAULT_MAX_UDP_PACKET_SIZE UDP_MAX_PACKET_SIZE
164 /* increase the UDP socket receive size to decrease the
165 * possibility of buffer overflow */
166 #define UDP_SOCKET_RECEIVE_SIZE_REQUEST (512 * 1024)
167
168 #define DEFAULT_IS_USING_SEQ_NUM 1
169
170 #ifdef WIN32
171 /* WINDOWS */
172 # define close closesocket
173 # define SOCK_ERR SOCKET_ERROR
174 #else
175 /* UNIX, VXWORKS */
176 # define INVALID_SOCKET (-1)
177 # define SOCK_ERR (-1)
178
179 typedef int SOCKET;
180 #endif
181
182 /* MIN utility */
183 #ifndef MIN
184 #define MIN(a,b) ((a) < (b) ? (a) : (b))
185 #endif
186
187 /***************** TYPEDEFS **************************************************/
188
189 #if (defined(_WIN32)) || (defined(VXWORKS))
190 /* socklen_t may not be available */
191 typedef int rtiostream_socklen_t;
192 #else
193 typedef socklen_t rtiostream_socklen_t;
194 #endif
195
196 /* Server specific data structure */
197 typedef struct ServerData_tag {
198 int port; /* port number associated with the server socket */
199 SOCKET listenSock; /* listening socket to accept incoming connections */
200 char *serverInfoFile; /* the filename that is used to write the server
201 port number when dynamic port allocation is used */
202 } ServerData;
203
204 /* UDP send / receive buffer data structure */
205 typedef struct UDPPacketBuffer_tag {
206 char * buffer; /* pointer to the buffer */
207 char * dataPtr; /* pointer to the current position in the buffer */
208 int dataAvail; /* amount of data in the buffer */
209 } UDPPacketBuffer;
210
211 /* Type for the optional UDP sequence number */
212 typedef uint32_T udpSeqNum_T;
213 /* byte size of the UDP sequence number */
214 #define UDP_SEQ_NUM_SIZE ((int) sizeof(udpSeqNum_T))
215
216 /* UDP specific data structure */
217 typedef struct UDPData_tag {
218 int isUsingSeqNum; /* is this connection using sequence numbers */
219 int maxPacketSize; /* max packet size (buffer size) */
220 UDPPacketBuffer * recvBuffer; /* buffer for an incoming datagram */
221 UDPPacketBuffer * sendBuffer; /* buffer for an outgoing datagram */
222 udpSeqNum_T sendSeqNum; /* sequence number to add to outgoing datagrams */
223 udpSeqNum_T expectedRecvSeqNum; /* expected sequence number in incoming
224 datagrams */
225 int resetExpectedRecvSeqNum; /* flags whether to reset expectedRecvSeqNum
226 to the sequence number of the next incoming
227 datagram */
228 } UDPData;
229
230 /* enum of supported communications protocols */
231 typedef enum {TCP_PROTOCOL, UDP_PROTOCOL} CommsProtocol;
232
233 /* Data encapsulating a single client / server connection */
234 typedef struct ConnectionData_tag {
235 int isInUse; /* is this ConnectionData instance currently in use? */
236 int isServer; /* is this ConnectionData instance a Server (or client)? */
237 int blockingRecvTimeout; /* Timeout value in seconds. rtIOStreamRecv
238 blocks until at least some of the requested
239 data is available or the timeout expires.
240 If a timeout occurs the receiveSize will be 0.
241
242 A value of BLOCKING_RECV_TIMEOUT_NOWAIT (0)
243 means to block for 0 seconds (polling mode).
244 rtIOStreamRecv processes
245 any pending data or, if no data is available,
246 returns immediately with a receiveSize of 0.
247
248 A value of BLOCKING_RECV_TIMEOUT_NEVER (-1)
249 means to block indefinitely (full blocking
250 mode). rtIOStreamRecv blocks
251 until at least some of the requested data is
252 available. receiveSize should always be
253 greater than 0.
254
255 A value of BLOCKING_RECV_TIMEOUT_10MS (-3)
256 means to block for up to 10 ms to avoid high CPU
257 load.
258 */
259 int isVerbose; /* flag indicating whether to display verbose output */
260 CommsProtocol protocol; /* TCP or UDP protocol */
261 SOCKET sock; /* socket to send/receive packets */
262 ServerData * serverData; /* Server specific data - NULL for clients */
263 UDPData * udpData; /* UDP specific data - NULL for TCP */
264 } ConnectionData;
265
266 /**************** LOCAL DATA *************************************************/
267
268 /* All local data resides in the per client /
269 * server instance ConnectionData structures to make sure each connection is
270 * completely independent.
271 *
272 * Each ConnectionData does not use much memory; any optionally required
273 * send / recv buffers are dynamically allocated and freed when the ConnectionData
274 * actually becomes in use.
275 *
276 * The static array will be deallocated when the shared library is unloaded.
277 *
278 * Using an array rather than a linked list allows us to have fast direct lookup
279 * of ConnectionData from connectionID during calls to rtIOStreamSend/Recv */
280 #define MAX_NUM_CONNECTIONS (50)
281 static ConnectionData connectionDataArray[MAX_NUM_CONNECTIONS];
282
283 /************** LOCAL FUNCTION PROTOTYPES ************************************/
284
285 static int initConnectionData(int connectionID,
286 int isServer,
287 CommsProtocol protocol,
288 SOCKET sock,
289 int blockingRecvTimeout,
290 int maxPacketSize,
291 int serverPort,
292 char * serverInfoFile,
293 int isVerbose,
294 int isUsingSeqNum);
295
296 static int getConnectionID(void);
297
298 static ConnectionData * getConnectionData(int connectionID);
299
300 static void freeConnectionData(ConnectionData * connection);
301
302 static UDPPacketBuffer * createUDPPacketBuffer(int maxPacketSize);
303
304 static void freeUDPPacketBuffer(UDPPacketBuffer ** udpPacketBuffer);
305
306 static void resetUDPPacketBuffer(UDPPacketBuffer * udpPacketBuffer);
307
308 static int processUDPRecvSeqNum(ConnectionData * connection);
309
310 static int initialUDPServerRecvfrom(ConnectionData * connection,
311 struct sockaddr * clientSA,
312 rtiostream_socklen_t * clientSALen);
313
314 static int waitForClientClose(ConnectionData * connection);
315
316 static int socketDataSet(
317 ConnectionData * connection,
318 const void *src,
319 const size_t size,
320 size_t *sizeSent);
321
322 static int socketDataGet(
323 ConnectionData * connection,
324 char *dst,
325 const size_t size,
326 size_t *sizeRecvd);
327
328 static int socketDataPending(
329 const SOCKET sock,
330 ConnectionData * connection,
331 int *outPending,
332 int timeoutSecs);
333
334 static int serverStreamRecv(
335 ConnectionData * connection,
336 void * dst,
337 size_t size,
338 size_t * sizeRecvd);
339
340 static SOCKET serverOpenSocket(int port, char * serverInfoFile, CommsProtocol protocol);
341
342 #if (!defined(VXWORKS))
343 static SOCKET clientOpenSocket(char * hostName, unsigned int portNum, CommsProtocol protocol);
344 #endif
345
346 static void serverAcceptSocket(ConnectionData * connection);
347
348 static int processArgs(
349 const int argc,
350 void * argv[],
351 char ** hostName,
352 unsigned int * portNum,
353 unsigned int * isClient,
354 int * isBlocking,
355 int * recvTimeout,
356 char ** serverInfoFile,
357 CommsProtocol * protocol,
358 int * maxPacketSize,
359 int * isVerbose,
360 int * isUsingSeqNum);
361
362 #if (!defined(VXWORKS))
363 static unsigned long nameLookup(char * hostName);
364 #endif
365
366 /*************** LOCAL FUNCTIONS **********************************************/
367
368 /* Function: initConnectionData =================================================
369 * Abstract:
370 * Initializes a client / server ConnectionData for the specified protocol.
371 *
372 * A return value of RTIOSTREAM_ERROR indicates an error.
373 */
374 static int initConnectionData(int connectionID,
375 int isServer,
376 CommsProtocol protocol,
377 SOCKET sock,
378 int blockingRecvTimeout,
379 int maxPacketSize,
380 int serverPort,
381 char * serverInfoFile,
382 int isVerbose,
383 int isUsingSeqNum) {
384 int retVal = RTIOSTREAM_NO_ERROR;
385 ConnectionData * connection = &connectionDataArray[connectionID];
386
387 /* initialize the new ConnectionData */
388 connection->isInUse = 1;
389 connection->isServer = isServer;
390 connection->blockingRecvTimeout = blockingRecvTimeout;
391 connection->protocol = protocol;
392 connection->isVerbose = isVerbose;
393 /* initialize to NULL early so that calls to
394 * freeConnectionData on error will succeed */
395 connection->udpData = NULL;
396 connection->serverData = NULL;
397
398 if (protocol == UDP_PROTOCOL) {
399 /* initialize the UDP data */
400 connection->udpData = malloc(sizeof(UDPData));
401 if (connection->udpData == NULL) {
402 printf("initConnectionData:UDPData malloc failed.\n");
403 freeConnectionData(connection);
404 retVal = RTIOSTREAM_ERROR;
405 return retVal;
406 }
407 /* initialize to NULL */
408 connection->udpData->recvBuffer = NULL;
409 connection->udpData->sendBuffer = NULL;
410 connection->udpData->isUsingSeqNum = isUsingSeqNum;
411 connection->udpData->maxPacketSize = maxPacketSize;
412 /* send sequence numbers always start from 0 */
413 connection->udpData->sendSeqNum = 0;
414 /* initially, seed the expectedRecvSeqNum from the first
415 * received packet */
416 connection->udpData->resetExpectedRecvSeqNum = 1;
417 connection->udpData->recvBuffer = createUDPPacketBuffer(maxPacketSize);
418 if (connection->udpData->recvBuffer == NULL) {
419 printf("initConnectionData:createUDPPacketBuffer failed.\n");
420 freeConnectionData(connection);
421 retVal = RTIOSTREAM_ERROR;
422 return retVal;
423 }
424 if (maxPacketSize > UDP_MAX_PACKET_SIZE) {
425 /* packet size cannot exceed the maximum
426 * UDP packet size */
427 printf("initConnectionData: udpmaxpacketsize must be less than %d\n", UDP_MAX_PACKET_SIZE);
428 freeConnectionData(connection);
429 retVal = RTIOSTREAM_ERROR;
430 return retVal;
431 }
432 if (connection->udpData->isUsingSeqNum) {
433 /* packet size must be larger than the size
434 * of the sequence number */
435 if (maxPacketSize <= UDP_SEQ_NUM_SIZE) {
436 printf("initConnectionData: udpmaxpacketsize must be larger than %d\n", UDP_SEQ_NUM_SIZE);
437 freeConnectionData(connection);
438 retVal = RTIOSTREAM_ERROR;
439 return retVal;
440 }
441 /* send buffer will be required in order to add the sequence
442 * number to the outgoing data */
443 connection->udpData->sendBuffer = createUDPPacketBuffer(maxPacketSize);
444 if (connection->udpData->sendBuffer == NULL) {
445 printf("initConnectionData:createUDPPacketBuffer failed.\n");
446 freeConnectionData(connection);
447 retVal = RTIOSTREAM_ERROR;
448 return retVal;
449 }
450 }
451 }
452
453 if (isServer) {
454 /* initialize server data */
455 connection->serverData = malloc(sizeof(ServerData));
456 if (connection->serverData == NULL) {
457 printf("initConnectionData:ServerData malloc failed.\n");
458 freeConnectionData(connection);
459 retVal = RTIOSTREAM_ERROR;
460 return retVal;
461 }
462 connection->serverData->port = serverPort;
463 connection->serverData->serverInfoFile = serverInfoFile;
464 /* provided sock is the listening sock */
465 connection->serverData->listenSock = sock;
466 /* later call to serverAcceptSocket will set sock */
467 connection->sock = INVALID_SOCKET;
468 }
469 else {
470 /* store the provided socket */
471 connection->sock = sock;
472 }
473
474 if (isVerbose) {
475 if (connection->protocol == TCP_PROTOCOL) {
476 printf("Connection id %d, protocol: TCP/IP\n", connectionID);
477 }
478 else {
479 printf("Connection id %d, protocol: UDP/IP\n", connectionID);
480 printf("Connection id %d, maxPacketSize: %d\n", connectionID,
481 connection->udpData->maxPacketSize);
482 printf("Connection id %d, isUsingSeqNum: %d\n", connectionID,
483 connection->udpData->isUsingSeqNum);
484 }
485 {
486 /* display the size of the socket receive buffer */
487 rtiostream_socklen_t optionLen = sizeof(int);
488 int optionValue;
489 getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *) &optionValue, &optionLen);
490 printf("Connection id %d, socket receive buffer size: %d\n", connectionID, optionValue);
491 }
492 printf("Connection id %d, blockingRecvTimeout: %d\n", connectionID,
493 connection->blockingRecvTimeout);
494 if (connection->isServer) {
495 printf("Connection id %d, type: server\n", connectionID);
496 if (connection->serverData->serverInfoFile != NULL) {
497 printf("Connection id %d, server info file: %s\n", connectionID,
498 connection->serverData->serverInfoFile);
499 }
500 }
501 else {
502 printf("Connection id %d, type: client\n", connectionID);
503 }
504 printf("Connection id %d, socket id %d\n", connectionID, (int) sock);
505 /* relevant to both clients and servers */
506 printf("Connection id %d, server port: %d\n", connectionID, serverPort);
507 }
508 return retVal;
509 }
510
511 /* Function: getConnectionData =================================================
512 * Abstract:
513 * Retrieves a ConnectionData instance given its connectionID
514 * (as returned by initConnectionData)
515 *
516 * NOTE: An invalid connectionID will lead to a NULL pointer being returned
517 */
518 static ConnectionData * getConnectionData(int connectionID) {
519 /* return NULL for invalid or uninitialized connectionIDs */
520 ConnectionData * connection = NULL;
521 if ((connectionID >= 0) && (connectionID < MAX_NUM_CONNECTIONS)) {
522 if (connectionDataArray[connectionID].isInUse) {
523 connection = &connectionDataArray[connectionID];
524 }
525 }
526 return connection;
527 }
528
529 /* Function: getConnectionID =================================================
530 * Abstract:
531 * Returns a connectionID corresponding to a ConnectionData that is not
532 * already in use.
533 *
534 * Returns RTIOSTREAM_ERROR if all available ConnectionData instances are
535 * already in use.
536 */
537 static int getConnectionID(void) {
538 int connectionID;
539 int foundUnusedConnectionData = 0;
540 /* linear search for an unused ConnectionData */
541 for (connectionID = 0; connectionID < MAX_NUM_CONNECTIONS; connectionID++) {
542 if (!connectionDataArray[connectionID].isInUse) {
543 foundUnusedConnectionData = 1;
544 break;
545 }
546 }
547 if (!foundUnusedConnectionData) {
548 /* all ConnectionData's are in use */
549 printf("getConnectionID: All %d available connections are in use.\n", MAX_NUM_CONNECTIONS);
550 connectionID = RTIOSTREAM_ERROR;
551 }
552 return connectionID;
553 }
554
555 /* Function: freeConnectionData =================================================
556 * Abstract:
557 * Frees memory associated with the ConnectionData referenced by connectionID.
558 * Marks the ConnectionData instance as no longer in use.
559 */
560 static void freeConnectionData(ConnectionData * connection) {
561 /* mark the ConnectionData as not in use */
562 connection->isInUse = 0;
563 /* free dynamic memory */
564 if (connection->protocol == UDP_PROTOCOL) {
565 freeUDPPacketBuffer(&connection->udpData->recvBuffer);
566 if (connection->udpData->isUsingSeqNum) {
567 freeUDPPacketBuffer(&connection->udpData->sendBuffer);
568 }
569 free(connection->udpData);
570 connection->udpData = NULL;
571 }
572 if (connection->isServer) {
573 free(connection->serverData);
574 connection->serverData = NULL;
575 }
576 }
577
578 /* Function: createUDPPacketBuffer =================================================
579 * Abstract:
580 * Allocates storage for and initializes a UDPPacketBuffer
581 */
582 static UDPPacketBuffer * createUDPPacketBuffer(int maxPacketSize) {
583 /* initialize the UDP packet buffer */
584 UDPPacketBuffer * udpPacketBuffer = malloc(sizeof(UDPPacketBuffer));
585 if (udpPacketBuffer == NULL) {
586 printf("createUDPPacketBuffer:UDPPacketBuffer malloc failed.\n");
587 return udpPacketBuffer;
588 }
589 /* allocate the buffer */
590 udpPacketBuffer->buffer = calloc(maxPacketSize, sizeof(char));
591 if (udpPacketBuffer->buffer == NULL) {
592 printf("createUDPPacketBuffer:UDPPacketBuffer buffer malloc failed.\n");
593 /* free everything we allocated */
594 free(udpPacketBuffer);
595 udpPacketBuffer = NULL;
596 return udpPacketBuffer;
597 }
598 resetUDPPacketBuffer(udpPacketBuffer);
599 return udpPacketBuffer;
600 }
601
602 /* Function: freeUDPPacketBuffer =================================================
603 * Abstract:
604 * Frees memory associated with the referenced UDPPacketBuffer
605 */
606 static void freeUDPPacketBuffer(UDPPacketBuffer ** udpPacketBuffer) {
607 if (*udpPacketBuffer != NULL) {
608 /* free the buffer */
609 free((*udpPacketBuffer)->buffer);
610 (*udpPacketBuffer)->buffer = NULL;
611 /* free the container */
612 free(*udpPacketBuffer);
613 *udpPacketBuffer = NULL;
614 }
615 }
616
617 /* Function: resetUDPPacketBuffer =================================================
618 * Abstract:
619 * Resets the referenced UDP Packet Buffer so that it is ready to receive fresh data
620 */
621 static void resetUDPPacketBuffer(UDPPacketBuffer * udpPacketBuffer) {
622 udpPacketBuffer->dataPtr = udpPacketBuffer->buffer;
623 udpPacketBuffer->dataAvail = 0;
624 }
625
626 /* Function: socketDataPending =================================================
627 * Abstract:
628 * Returns true, via the 'pending' arg, if data is pending on the comm line.
629 * Returns false otherwise.
630 *
631 * RTIOSTREAM_NO_ERROR is returned on success, RTIOSTREAM_ERROR on failure.
632 */
633 static int socketDataPending(
634 const SOCKET sock,
635 ConnectionData * connection,
636 int *outPending,
637 int timeoutSecs)
638 {
639 fd_set ReadFds;
640 int pending;
641 struct timeval tval;
642 struct timeval * tvalPtr;
643 int retVal = RTIOSTREAM_NO_ERROR;
644
645 if (connection->protocol == UDP_PROTOCOL) {
646 /* first check the UDP buffer */
647 UDPPacketBuffer * udpPacketBuffer = connection->udpData->recvBuffer;
648 if (udpPacketBuffer->dataAvail) {
649 *outPending = 1;
650 return retVal;
651 }
652 }
653
654 FD_ZERO(&ReadFds);
655 FD_SET(sock, &ReadFds);
656
657 switch (timeoutSecs) {
658 case BLOCKING_RECV_TIMEOUT_NEVER:
659 /* specify null pointer for blocking */
660 tvalPtr = NULL;
661 break;
662 case BLOCKING_RECV_TIMEOUT_10MS:
663 /* set up the 10ms time-val */
664 tval.tv_sec = 0;
665 tval.tv_usec = 10000;
666 tvalPtr = &tval;
667 break;
668 default:
669 /* set up the time-val */
670 tval.tv_sec = timeoutSecs;
671 tval.tv_usec = 0;
672 tvalPtr = &tval;
673 break;
674 }
675
676 /*
677 * Casting the first arg to int removes warnings on windows 64-bit
678 * platform. It is safe to cast a SOCKET to an int here because on
679 * Linux SOCKET is typedef'd to int and on windows the first argument
680 * to select is ignored (so it doesn't matter what the value is).
681 */
682 pending = select((int)(sock + 1), &ReadFds, NULL, NULL, tvalPtr);
683 if (pending == SOCK_ERR) {
684 retVal = RTIOSTREAM_ERROR;
685 }
686
687 *outPending = (pending==1);
688 return(retVal);
689
690 } /* end socketDataPending */
691
692 /* Function: initialUDPServerRecvfrom =====================================================
693 * Abstract:
694 * Reads data from the client via "recvfrom" into the UDP packet buffer.
695 * The client sockaddr is returned via clientSA and clientSALen.
696 *
697 * RTIOSTREAM_NO_ERROR is returned on success, RTIOSTREAM_ERROR is returned on
698 * failure.
699 */
700 static int initialUDPServerRecvfrom(ConnectionData * connection,
701 struct sockaddr * clientSA,
702 rtiostream_socklen_t * clientSALen) {
703 int nRead;
704 int retVal;
705 UDPPacketBuffer * udpPacketBuffer = connection->udpData->recvBuffer;
706 /* reset */
707 resetUDPPacketBuffer(udpPacketBuffer);
708 /* initialize ahead of call to recvfrom */
709 *clientSALen = sizeof(*clientSA);
710 /* read into UDP buffer from the listenSock
711 * and get sockaddr of the client */
712 nRead = recvfrom(connection->serverData->listenSock,
713 udpPacketBuffer->dataPtr,
714 connection->udpData->maxPacketSize,
715 0U,
716 clientSA,
717 clientSALen);
718
719 if (nRead == SOCK_ERR) {
720 retVal = RTIOSTREAM_ERROR;
721 } else {
722 /* set dataAvail */
723 udpPacketBuffer->dataAvail = nRead;
724 /* handle optional sequence number */
725 retVal = processUDPRecvSeqNum(connection);
726 }
727 return retVal;
728 }
729
730 /* Function: processUDPRecvSeqNum =====================================================
731 * Abstract:
732 * Processes sequence numbers in received UDP datagrams.
733 *
734 * RTIOSTREAM_NO_ERROR is returned on success, RTIOSTREAM_ERROR is returned on
735 * failure.
736 */
737 static int processUDPRecvSeqNum(ConnectionData * connection) {
738 int retVal = RTIOSTREAM_NO_ERROR;
739 if (connection->udpData->isUsingSeqNum) {
740 UDPPacketBuffer * udpPacketBuffer = connection->udpData->recvBuffer;
741 /* process sequence number */
742 udpSeqNum_T recvSeqNum;
743 if (udpPacketBuffer->dataAvail < UDP_SEQ_NUM_SIZE) {
744 printf("No receive sequence number found.\n");
745 retVal = RTIOSTREAM_ERROR;
746 return retVal;
747 }
748 /* read sequence number from the buffer
749 *
750 * sequence number is always transmitted / received in
751 * host Endian */
752 memcpy(&recvSeqNum,
753 udpPacketBuffer->dataPtr,
754 UDP_SEQ_NUM_SIZE);
755 udpPacketBuffer->dataPtr += UDP_SEQ_NUM_SIZE;
756 udpPacketBuffer->dataAvail -= UDP_SEQ_NUM_SIZE;
757 if (connection->isVerbose) {
758 printf("Received UDP packet with sequence number: %u\n", recvSeqNum);
759 }
760 if (connection->udpData->resetExpectedRecvSeqNum) {
761 /* reset the expected sequence number */
762 connection->udpData->expectedRecvSeqNum = recvSeqNum + 1;
763 connection->udpData->resetExpectedRecvSeqNum = 0;
764 }
765 else {
766 /* compare with expected receive seq num */
767 if (recvSeqNum != connection->udpData->expectedRecvSeqNum) {
768 printf("UDP packet sequence number mismatch. Expected #: %d, Actual #: %d\n",
769 connection->udpData->expectedRecvSeqNum, recvSeqNum);
770 retVal = RTIOSTREAM_ERROR;
771 }
772 else {
773 /* increment expected receive seq num */
774 connection->udpData->expectedRecvSeqNum++;
775 }
776 }
777 }
778 return retVal;
779 }
780
781 /* Function: socketDataGet =====================================================
782 * Abstract:
783 * Attempts to gets the specified number of bytes from the specified socket.
784 * The number of bytes read is returned via the 'sizeRecvd' parameter.
785 * RTIOSTREAM_NO_ERROR is returned on success, RTIOSTREAM_ERROR is returned on
786 * failure.
787 *
788 * NOTES:
789 * o it is not an error for 'sizeRecvd' to be returned as 0
790 * o this function blocks if no data is available
791 */
792 static int socketDataGet(ConnectionData * connection,
793 char *dst,
794 const size_t size,
795 size_t *sizeRecvd)
796 {
797 int nRead = 0;
798 int retVal = RTIOSTREAM_NO_ERROR;
799 /* Ensure size is not out of range for socket API recv function */
800 int sizeLim = (int) MIN(size, INT_MAX);
801
802 if (connection->protocol == TCP_PROTOCOL) {
803 nRead = recv(connection->sock, dst, sizeLim, 0U);
804 if (nRead == SOCK_ERR) {
805 retVal = RTIOSTREAM_ERROR;
806 } else {
807 retVal = RTIOSTREAM_NO_ERROR;
808 }
809 }
810 else {
811 UDPPacketBuffer * udpPacketBuffer = connection->udpData->recvBuffer;
812 /* receive more data in to the buffer if required */
813 if (udpPacketBuffer->dataAvail == 0) {
814 /* reset */
815 resetUDPPacketBuffer(udpPacketBuffer);
816 /* read into buffer */
817 nRead = recv(connection->sock,
818 udpPacketBuffer->dataPtr,
819 connection->udpData->maxPacketSize,
820 0U);
821
822 if (nRead == SOCK_ERR) {
823 retVal = RTIOSTREAM_ERROR;
824 } else {
825 udpPacketBuffer->dataAvail = nRead;
826 /* handle optional sequence number */
827 retVal = processUDPRecvSeqNum(connection);
828 if (retVal == RTIOSTREAM_ERROR) {
829 return retVal;
830 }
831 }
832 }
833 /* get data from the buffer */
834 /* for the special case where we request a */
835 /* size of 0 bytes, return the whole buffer */
836 if (udpPacketBuffer->dataAvail) {
837 if (size == 0) {
838 nRead = udpPacketBuffer->dataAvail;
839 } else {
840 nRead = MIN(udpPacketBuffer->dataAvail, sizeLim);
841 }
842 memcpy(dst, (void *) udpPacketBuffer->dataPtr, nRead);
843 udpPacketBuffer->dataAvail -= nRead;
844 udpPacketBuffer->dataPtr += nRead;
845 }
846 }
847
848 if (retVal!=RTIOSTREAM_ERROR) {
849 *sizeRecvd = (size_t) nRead;
850 }
851
852 return retVal;
853 } /* end socketDataGet */
854
855
856 /* Function: socketDataSet =====================================================
857 * Abstract:
858 * Utility function to send data via the specified socket
859 */
860 static int socketDataSet(
861 ConnectionData * connection,
862 const void *src,
863 const size_t size,
864 size_t *sizeSent)
865 {
866 int nSent;
867 int retVal = RTIOSTREAM_NO_ERROR;
868 const void *sendSrc = src;
869
870 /* Ensure size is not out of range for socket API send function */
871 int sizeLim = (int) MIN(size, INT_MAX);
872
873 if (connection->protocol == UDP_PROTOCOL) {
874 /* limit sends according to max packet size */
875 int maxPacketSize = connection->udpData->maxPacketSize;
876 if (connection->udpData->isUsingSeqNum) {
877 int transferAmount;
878 UDPPacketBuffer * udpPacketBuffer = connection->udpData->sendBuffer;
879 /* need to apply sequence number and then increment it */
880 resetUDPPacketBuffer(udpPacketBuffer);
881 /* set data src */
882 sendSrc = udpPacketBuffer->dataPtr;
883 /* add sequence number to the buffer
884 *
885 * sequence number is always transmitted / received in
886 * host Endian */
887 memcpy(udpPacketBuffer->dataPtr,
888 &connection->udpData->sendSeqNum,
889 UDP_SEQ_NUM_SIZE);
890 udpPacketBuffer->dataPtr += UDP_SEQ_NUM_SIZE;
891 udpPacketBuffer->dataAvail += UDP_SEQ_NUM_SIZE;
892 /* copy the data - don't overflow the packet buffer */
893 transferAmount = MIN(sizeLim, maxPacketSize - udpPacketBuffer->dataAvail);
894 memcpy(udpPacketBuffer->dataPtr,
895 src,
896 transferAmount);
897 udpPacketBuffer->dataAvail += transferAmount;
898 sizeLim = udpPacketBuffer->dataAvail;
899 }
900 else {
901 sizeLim = MIN(maxPacketSize, sizeLim);
902 }
903 }
904
905 #ifndef VXWORKS
906 nSent = send(connection->sock, sendSrc, sizeLim, 0);
907 #else
908 /*
909 * VXWORKS send prototype does not have src as const. This suppresses
910 * the compiler warning.
911 */
912 nSent = send(connection->sock, (char *)sendSrc, sizeLim, 0);
913 #endif
914 if (nSent == SOCK_ERR) {
915 retVal = RTIOSTREAM_ERROR;
916 } else {
917 if ((connection->protocol == UDP_PROTOCOL) &&
918 (connection->udpData->isUsingSeqNum) &&
919 (nSent > 0)) {
920 if (nSent < (int) UDP_SEQ_NUM_SIZE) {
921 /* expected the sequence number to have transmitted */
922 retVal = RTIOSTREAM_ERROR;
923 return retVal;
924 }
925 else {
926 if (connection->isVerbose) {
927 printf("Sent UDP packet with sequence number: %u\n", connection->udpData->sendSeqNum);
928 }
929 /* increment sequence number */
930 connection->udpData->sendSeqNum++;
931 nSent -= UDP_SEQ_NUM_SIZE;
932 }
933 }
934 *sizeSent = (size_t)nSent;
935 }
936
937 return retVal;
938 }
939
940 /* Function: serverStreamRecv =================================================
941 * Abstract:
942 * Send data from the server-side
943 */
944 static int serverStreamRecv(
945 ConnectionData * connection,
946 void * dst,
947 size_t size,
948 size_t * sizeRecvd)
949 {
950 int retVal = RTIOSTREAM_NO_ERROR;
951 *sizeRecvd = 0;
952
953 if (connection->sock == INVALID_SOCKET) {
954 /* Attempt to open connection */
955 serverAcceptSocket(connection);
956 }
957
958 if (connection->sock != INVALID_SOCKET) {
959 int pending;
960 if (connection->blockingRecvTimeout != BLOCKING_RECV_TIMEOUT_NEVER) {
961 /* only call costly "select" if necessary */
962 retVal = socketDataPending(connection->sock,
963 connection,
964 &pending,
965 connection->blockingRecvTimeout);
966 }
967 else {
968 /* block in "recv" if necessary */
969 pending = 1;
970 }
971
972 if ( (pending !=0) && (retVal==RTIOSTREAM_NO_ERROR) && (size>0) ) {
973
974 retVal = socketDataGet(connection, (char *)dst, size, sizeRecvd);
975
976 if (*sizeRecvd == 0) {
977
978 if (errno == RTIOSTREAM_ECONNRESET) {
979 /* If we are closing the connection and we received this
980 * error, it means the other side of the connection was
981 * already closed. Since we are expecting this, we can
982 * ignore this particular error.
983 */
984 retVal = RTIOSTREAM_NO_ERROR;
985 } else {
986 /* Connection closed gracefully by client */
987 }
988
989 close(connection->sock);
990 connection->sock = INVALID_SOCKET;
991 }
992 }
993
994 if ( retVal == RTIOSTREAM_ERROR ) {
995 close(connection->sock);
996 connection->sock = INVALID_SOCKET;
997 }
998 }
999
1000 return retVal;
1001 }
1002
1003 /* Function: serverOpenSocket =================================================
1004 * Abstract:
1005 * Opens the listening socket to be used for accepting an incoming connection.
1006 */
1007 static SOCKET serverOpenSocket(int port, char * serverInfoFile, CommsProtocol protocol)
1008 {
1009
1010 struct sockaddr_in serverAddr;
1011 int sockStatus;
1012 rtiostream_socklen_t sFdAddSize = (rtiostream_socklen_t) sizeof(struct sockaddr_in);
1013 SOCKET lFd;
1014 int option;
1015
1016 /*
1017 * Create a TCP or UDP based socket.
1018 */
1019 memset((void *) &serverAddr,0,(size_t)sFdAddSize);
1020 serverAddr.sin_family = AF_INET;
1021 serverAddr.sin_port = htons((unsigned short int) port);
1022 serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
1023
1024 if (protocol == TCP_PROTOCOL) {
1025 lFd = socket(AF_INET, SOCK_STREAM, 0);
1026 }
1027 else {
1028 lFd = socket(AF_INET, SOCK_DGRAM, 0);
1029 }
1030
1031 if (lFd == INVALID_SOCKET) {
1032 printf("socket() call failed.\n");
1033 } else {
1034 /*
1035 * Listening socket should always use the SO_REUSEADDR option
1036 * ("Unix Network Programming - Networking APIs:Sockets and XTI",
1037 * Volume 1, 2nd edition, by W. Richard Stevens).
1038 */
1039 option = 1;
1040 sockStatus =
1041 setsockopt(lFd,SOL_SOCKET,SO_REUSEADDR,(char*)&option,sizeof(option));
1042 if (sockStatus == SOCK_ERR) {
1043 printf("setsocketopt() call failed.\n");
1044 close(lFd);
1045 lFd = INVALID_SOCKET;
1046 }
1047 if (protocol == TCP_PROTOCOL) {
1048 /* Disable Nagle's Algorithm*/
1049 option = 1;
1050 sockStatus =
1051 setsockopt(lFd,IPPROTO_TCP,TCP_NODELAY,(char*)&option,sizeof(option));
1052 if (sockStatus == SOCK_ERR) {
1053 printf("setsocketopt() TCP_NODELAY call failed.\n");
1054 close(lFd);
1055 lFd = INVALID_SOCKET;
1056 }
1057 }
1058 else {
1059 /* increase the UDP socket receive size to decrease the
1060 * possibility of buffer overflow */
1061 option = UDP_SOCKET_RECEIVE_SIZE_REQUEST;
1062 sockStatus =
1063 setsockopt(lFd, SOL_SOCKET, SO_RCVBUF,(char*)&option, sizeof(option));
1064 if (sockStatus == SOCK_ERR) {
1065 printf("setsocketopt() SO_RCVBUF call failed.\n");
1066 close(lFd);
1067 lFd = INVALID_SOCKET;
1068 }
1069 }
1070 }
1071
1072 if (lFd != INVALID_SOCKET) {
1073 sockStatus = bind(lFd, (struct sockaddr *) &serverAddr, sFdAddSize);
1074 if (sockStatus == SOCK_ERR) {
1075 printf("bind() call failed: %s\n", strerror(errno));
1076 close(lFd);
1077 lFd = INVALID_SOCKET;
1078 }
1079 }
1080
1081 if (lFd != INVALID_SOCKET) {
1082 if (port == 0) {
1083 /* port 0 specifies dynamic free port allocation
1084 * reuse serverAddr to store the actual address / port */
1085 sockStatus = getsockname(lFd, (struct sockaddr *) &serverAddr, &sFdAddSize);
1086 if (sockStatus == SOCK_ERR) {
1087 fprintf(stderr,"getsockname() call failed: %s\n", strerror(errno));
1088 close(lFd);
1089 lFd = INVALID_SOCKET;
1090 } else {
1091 if(serverInfoFile != NULL) {
1092 FILE* fh;
1093
1094 /* Open file in append mode to save info already stored in the file*/
1095 fh = fopen(serverInfoFile,"a");
1096 #ifdef __LCC64__
1097 /* This is needed due to an issue with LCC64, see the following geck: g919889 */
1098 fseek ( fh, 0 , SEEK_END );
1099 #endif
1100 if (fh == NULL) {
1101 fprintf(stderr,"Unable to open output file to write server port number: %s\n", strerror(errno));
1102 lFd = INVALID_SOCKET;
1103 }
1104
1105 (void)fprintf(fh, "Server Port Number: %u\n", ntohs(serverAddr.sin_port));
1106 fclose(fh);
1107 } else {
1108 /* write the server port number to stdout */
1109 SERVER_PORT_PRINTF("Server Port Number: %u\n", ntohs(serverAddr.sin_port));
1110 }
1111 }
1112 }
1113 }
1114 if (protocol == TCP_PROTOCOL) {
1115 if (lFd != INVALID_SOCKET) {
1116 sockStatus = listen(lFd, 2);
1117 if (sockStatus == SOCK_ERR) {
1118 printf("listen() call failed.\n");
1119 close(lFd);
1120 lFd = INVALID_SOCKET;
1121 }
1122 }
1123 }
1124 return lFd;
1125 }
1126 /* Function: serverAcceptSocket =================================================
1127 * Abstract:
1128 * Called when the target is not currently connected to the host, this
1129 * function attempts to open the connection.
1130 *
1131 * In the case of sockets, this is a passive operation in that the host
1132 * initiates contact, the target simply listens for connection requests.
1133 *
1134 * NOTES:
1135
1136 * Blocks according to blockingRecvTimeout. When
1137 * polling, there may be no open requests pending. In this case, this
1138 * function returns without making a connection; this is not an error.
1139 */
1140 static void serverAcceptSocket(ConnectionData * connection)
1141 {
1142 struct sockaddr_in clientAddr;
1143 rtiostream_socklen_t sFdAddSize = sizeof(struct sockaddr_in);
1144 SOCKET cFd = INVALID_SOCKET;
1145 int error = RTIOSTREAM_NO_ERROR;
1146 int pending;
1147
1148 /* Check that the listening socket is still valid and open a new socket if
1149 * not */
1150 if (connection->serverData->listenSock == INVALID_SOCKET) {
1151 connection->serverData->listenSock = serverOpenSocket(connection->serverData->port,
1152 connection->serverData->serverInfoFile,
1153 connection->protocol);
1154 }
1155
1156 /* pass listenSock rather than sock */
1157 error = socketDataPending(connection->serverData->listenSock,
1158 connection,
1159 &pending,
1160 connection->blockingRecvTimeout);
1161
1162 if ( (pending > 0) && (error==RTIOSTREAM_NO_ERROR) ) {
1163 if (connection->protocol == TCP_PROTOCOL) {
1164 /*
1165 * Wait to accept a connection on the comm socket.
1166 */
1167 cFd = accept(connection->serverData->listenSock,
1168 (struct sockaddr *)&clientAddr,
1169 &sFdAddSize);
1170
1171 if (cFd == INVALID_SOCKET) {
1172 printf("accept() for comm socket failed.\n");
1173 error = RTIOSTREAM_ERROR;
1174 }
1175
1176 if (error == RTIOSTREAM_ERROR) {
1177 close(connection->serverData->listenSock);
1178 connection->serverData->listenSock = INVALID_SOCKET;
1179 }
1180 }
1181 else {
1182 /* UDP - data is pending */
1183 struct sockaddr clientSA;
1184 rtiostream_socklen_t clientSALen;
1185 /* new connection, make sure we reset expectedRecvSeqNum,
1186 * if sequence numbers are in use */
1187 connection->udpData->resetExpectedRecvSeqNum = 1;
1188 /* Do the initial UDP server "recvfrom" to get the
1189 * client sockaddr. Data read will be placed
1190 * ready in the UDP packet buffer. */
1191 error = initialUDPServerRecvfrom(connection, &clientSA, &clientSALen);
1192 if (error == RTIOSTREAM_ERROR) {
1193 close(connection->serverData->listenSock);
1194 connection->serverData->listenSock = INVALID_SOCKET;
1195 printf("initialUDPServerRecvfrom() failed.\n");
1196 }
1197 else {
1198 /* connect exclusively to the client so we no longer
1199 * have to use recvfrom / sendto */
1200 if (connect(connection->serverData->listenSock,
1201 &clientSA,
1202 clientSALen) == SOCK_ERR) {
1203 close(connection->serverData->listenSock);
1204 connection->serverData->listenSock = INVALID_SOCKET;
1205 printf("Server connect() failed.\n");
1206 }
1207 }
1208 /* for UDP, the socket and listening socket are the same */
1209 cFd = connection->serverData->listenSock;
1210 }
1211 }
1212 /* set sock */
1213 connection->sock = cFd;
1214 }
1215
1216
1217 /* Function: nameLookup =======================
1218 * Lookup target network name.
1219 */
1220 #if (!defined(VXWORKS))
1221 static unsigned long nameLookup(char * hostName) {
1222
1223 struct hostent * hp = NULL;
1224 struct in_addr * iaddr = NULL;
1225 unsigned long addr = INADDR_NONE;
1226
1227 /*
1228 * Default to localhost if hostname not specified.
1229 */
1230 if (hostName == NULL) {
1231 static char localhost[] = "localhost";
1232 hostName = localhost;
1233 }
1234
1235 /*
1236 * See if the address is an IPV4 dot separated address:
1237 */
1238 addr = inet_addr(hostName);
1239
1240 if (addr == INADDR_NONE) {
1241 /* Since the address is not an IPV4 dot separated address,
1242 * do a name lookup to get this:
1243 */
1244 hp = gethostbyname(hostName);
1245 if (hp == NULL) {
1246 printf("gethostbyname() call failed.\n");
1247 addr = INADDR_NONE;
1248 } else {
1249 iaddr = (struct in_addr *) hp->h_addr;
1250 addr = iaddr->s_addr;
1251 }
1252 }
1253 return(addr);
1254 }
1255 #endif
1256
1257 /* Function: processArgs ====================================================
1258 * Abstract:
1259 * Process the arguments specified by the user when opening the rtIOStream.
1260 *
1261 * If any unrecognized options are encountered, ignore them.
1262 *
1263 * Returns zero if successful or RTIOSTREAM_ERROR if
1264 * an error occurred.
1265 *
1266 * o IMPORTANT!!!
1267 * As the arguments are processed, their strings should be NULL'd out in
1268 * the argv array.
1269 */
1270 static int processArgs(
1271 const int argc,
1272 void * argv[],
1273 char ** hostName,
1274 unsigned int * portNum,
1275 unsigned int * isClient,
1276 int * isBlocking,
1277 int * recvTimeout,
1278 char ** serverInfoFile,
1279 CommsProtocol * protocol,
1280 int * maxPacketSize,
1281 int * isVerbose,
1282 int * isUsingSeqNum)
1283 {
1284 int retVal = RTIOSTREAM_NO_ERROR;
1285 int count = 0;
1286
1287 while(count < argc) {
1288 const char *option = (char *)argv[count];
1289 count++;
1290
1291 if (option != NULL) {
1292
1293 if ((strcmp(option, "-hostname") == 0) && (count != argc)) {
1294
1295 *hostName = (char *)argv[count];
1296 count++;
1297 argv[count-2] = NULL;
1298 argv[count-1] = NULL;
1299
1300 } else if ((strcmp(option, "-port") == 0) && (count != argc)) {
1301 char tmpstr[2];
1302 int itemsConverted;
1303 const char *portStr = (char *)argv[count];
1304
1305 count++;
1306
1307 itemsConverted = sscanf(portStr,"%d%1s", (int *) portNum, tmpstr);
1308 if ( (itemsConverted != 1) ||
1309 ( ((*portNum != 0) && (*portNum < 255)) || (*portNum > 65535))
1310 ) {
1311
1312 retVal = RTIOSTREAM_ERROR;
1313 } else {
1314
1315 argv[count-2] = NULL;
1316 argv[count-1] = NULL;
1317 }
1318
1319 } else if ((strcmp(option, "-client") == 0) && (count != argc)) {
1320
1321 *isClient = ( strcmp( (char *)argv[count], "1") == 0 );
1322
1323 count++;
1324 argv[count-2] = NULL;
1325 argv[count-1] = NULL;
1326
1327 } else if ((strcmp(option, "-blocking") == 0) && (count != argc)) {
1328
1329 *isBlocking = ( strcmp( (char *)argv[count], "1") == 0 );
1330
1331 count++;
1332 argv[count-2] = NULL;
1333 argv[count-1] = NULL;
1334
1335 } else if ((strcmp(option, "-verbose") == 0) && (count != argc)) {
1336
1337 *isVerbose = ( strcmp( (char *)argv[count], "1") == 0 );
1338
1339 count++;
1340 argv[count-2] = NULL;
1341 argv[count-1] = NULL;
1342
1343 } else if ((strcmp(option, "-recv_timeout_secs") == 0) && (count != argc)) {
1344 char tmpstr[2];
1345 int itemsConverted;
1346 const char *timeoutSecsStr = (char *)argv[count];
1347
1348 count++;
1349
1350 itemsConverted = sscanf(timeoutSecsStr,"%d%1s", (int *) recvTimeout, tmpstr);
1351 if ( itemsConverted != 1 ) {
1352 retVal = RTIOSTREAM_ERROR;
1353 } else {
1354
1355 argv[count-2] = NULL;
1356 argv[count-1] = NULL;
1357 }
1358
1359 } else if((strcmp(option, "-server_info_file") == 0) && (count != argc)) {
1360 *serverInfoFile= (char *) argv[count];
1361
1362 count++;
1363 argv[count-2] = NULL;
1364 argv[count-1] = NULL;
1365 } else if ((strcmp(option, "-protocol") == 0) && (count != argc)) {
1366 char * protocolStr = (char *) argv[count];
1367 count++;
1368 argv[count-2] = NULL;
1369 argv[count-1] = NULL;
1370 /* initialize dependent properties */
1371 *isUsingSeqNum = 0;
1372 /* process protocolStr */
1373 if (strcmp(protocolStr, TCP_PROTOCOL_STRING) == 0) {
1374 *protocol = TCP_PROTOCOL;
1375 }
1376 else if (strcmp(protocolStr, UDP_PROTOCOL_STRING) == 0) {
1377 *protocol = UDP_PROTOCOL;
1378 }
1379 else if (strcmp(protocolStr, UDP_PACKET_LOSS_DETECTON_PROTOCOL_STRING) == 0) {
1380 *protocol = UDP_PROTOCOL;
1381 /* enable sequence number protocol */
1382 *isUsingSeqNum = 1;
1383 }
1384 else {
1385 /* unrecognized protocol */
1386 retVal = RTIOSTREAM_ERROR;
1387 }
1388 } else if ((strcmp(option, "-udpmaxpacketsize") == 0) && (count != argc)) {
1389 char tmpstr[2];
1390 int itemsConverted;
1391 const char *maxUPDSizeStr = (char *)argv[count];
1392
1393 count++;
1394
1395 itemsConverted = sscanf(maxUPDSizeStr,"%d%1s", maxPacketSize, tmpstr);
1396 if ( itemsConverted != 1 ) {
1397 retVal = RTIOSTREAM_ERROR;
1398 } else {
1399 argv[count-2] = NULL;
1400 argv[count-1] = NULL;
1401 }
1402 } else{
1403 /* do nothing */
1404 }
1405 }
1406 }
1407 return retVal;
1408 }
1409
1410 /* Function: clientOpenSocket =================================================
1411 * Abstract:
1412 * Open a connection as Client
1413 */
1414 #if (!defined(VXWORKS))
1415 static SOCKET clientOpenSocket(char * hostName, unsigned int portNum, CommsProtocol protocol) {
1416
1417 struct sockaddr_in sa;
1418 unsigned long addr = INADDR_NONE;
1419 int errStatus = RTIOSTREAM_NO_ERROR;
1420 SOCKET cSock = INVALID_SOCKET;
1421
1422 addr = nameLookup(hostName);
1423
1424 if (addr!=INADDR_NONE) {
1425
1426 sa.sin_addr.s_addr = addr;
1427 sa.sin_family = AF_INET; /*hp->h_addrtype;*/
1428 sa.sin_port = htons((unsigned short) portNum);
1429
1430 /*
1431 * Create the sockets & make connections.
1432 */
1433 if (protocol == TCP_PROTOCOL) {
1434 cSock = socket(PF_INET,SOCK_STREAM,0);
1435 }
1436 else {
1437 cSock = socket(PF_INET,SOCK_DGRAM,0);
1438 }
1439 if (cSock == INVALID_SOCKET) {
1440 errStatus = RTIOSTREAM_ERROR;
1441 printf("socket() call failed for comm socket.\n");
1442 }
1443 } else {
1444 errStatus = RTIOSTREAM_ERROR;
1445 }
1446
1447 if ((errStatus!=RTIOSTREAM_ERROR) &&
1448 (protocol == UDP_PROTOCOL)) {
1449 /* increase the UDP socket receive size to decrease the
1450 * possibility of buffer overflow */
1451 int option = UDP_SOCKET_RECEIVE_SIZE_REQUEST;
1452 int sockStatus =
1453 setsockopt(cSock, SOL_SOCKET, SO_RCVBUF,(char*)&option, sizeof(option));
1454 if (sockStatus == SOCK_ERR) {
1455 printf("setsocketopt() SO_RCVBUF call failed.\n");
1456 cSock = INVALID_SOCKET;
1457 errStatus = RTIOSTREAM_ERROR;
1458 }
1459 }
1460
1461 if (errStatus!=RTIOSTREAM_ERROR) {
1462 if (connect(cSock, (struct sockaddr *)&sa, sizeof(sa)) == SOCK_ERR) {
1463 char tmp[1024];
1464
1465 sprintf(tmp,
1466 "Attempting to establish connection with hostname '%s' "
1467 "through port %d.\n",
1468 hostName,
1469 ntohs(sa.sin_port));
1470 cSock = INVALID_SOCKET;
1471 printf("%s",tmp);
1472 }
1473 }
1474
1475 return cSock;
1476 }
1477 #endif
1478
1479 /* Function: waitForClientClose =============================================
1480 * Abstract:
1481 *
1482 * Allow the client to close its end of the socket connection before the server
1483 * closes its own socket.
1484 *
1485 * The server will receive any outstanding data on the socket. When the server
1486 * receives 0 bytes, it indicates that it has acknowledged that the client
1487 * is closing its socket (this is essential for the client to complete
1488 * closing its socket without error) or that it timed out waiting for the client to
1489 * close its socket.
1490 *
1491 */
1492 static int waitForClientClose(ConnectionData * connection) {
1493 int retVal = RTIOSTREAM_NO_ERROR;
1494 #define TMP_BUF_SIZE (40)
1495 char * tmpBuf[TMP_BUF_SIZE];
1496 size_t numRecvd;
1497 /* cache the original blockingRecvTimeout */
1498 int savedBlockingRecvTimeout = connection->blockingRecvTimeout;
1499 /* wait time for client to close its socket */
1500 connection->blockingRecvTimeout = BLOCKING_RECV_TIMEOUT_SOCK_SHUTDOWN;
1501 do {
1502 retVal = serverStreamRecv(connection, (void *) tmpBuf, TMP_BUF_SIZE, &numRecvd);
1503 } while ((numRecvd > 0) && (retVal == RTIOSTREAM_NO_ERROR));
1504 /* restore blockingRecvTimeout */
1505 connection->blockingRecvTimeout = savedBlockingRecvTimeout;
1506 #undef TMP_BUF_SIZE
1507 return retVal;
1508 }
1509
1510 /***************** VISIBLE FUNCTIONS ******************************************/
1511
1512 /* Function: rtIOStreamOpen =================================================
1513 * Abstract:
1514 * Open the connection with the target.
1515 */
1516 int rtIOStreamOpen(int argc, void * argv[])
1517 {
1518 char *xHostName = NULL; /* default */
1519 char *serverInfoFile = NULL; /* default */
1520 unsigned int xPortNum = (SERVER_PORT_NUM); /* default */
1521 unsigned int isClient = 0; /* default */
1522 CommsProtocol protocol = DEFAULT_PROTOCOL;
1523 int isBlockingRecv = EXT_BLOCKING; /* default */
1524 int blockingRecvTimeout = DEFAULT_BLOCKING_RECV_TIMEOUT; /* rogue value */
1525 int maxPacketSize = DEFAULT_MAX_UDP_PACKET_SIZE;
1526 int isVerbose = DEFAULT_IS_VERBOSE;
1527 int isUsingSeqNum = DEFAULT_IS_USING_SEQ_NUM;
1528 int result = RTIOSTREAM_NO_ERROR;
1529 int streamID;
1530 SOCKET sock = INVALID_SOCKET;
1531
1532 /* determine the streamID for this new connection */
1533 streamID = getConnectionID();
1534 if (streamID == RTIOSTREAM_ERROR) {
1535 result = RTIOSTREAM_ERROR;
1536 return result;
1537 }
1538
1539 result = processArgs(argc, argv,
1540 &xHostName,
1541 &xPortNum,
1542 &isClient,
1543 &isBlockingRecv,
1544 &blockingRecvTimeout,
1545 &serverInfoFile,
1546 &protocol,
1547 &maxPacketSize,
1548 &isVerbose,
1549 &isUsingSeqNum);
1550 if (result == RTIOSTREAM_ERROR) {
1551 return result;
1552 }
1553
1554 if (isVerbose) {
1555 printf("rtIOStreamOpen\n");
1556 }
1557
1558 if (isBlockingRecv) {
1559 /* blocking: if blockingRecvTimeout has not been set, initialize to the client or
1560 * server specific default */
1561 if ((blockingRecvTimeout == DEFAULT_BLOCKING_RECV_TIMEOUT) ||
1562 (blockingRecvTimeout < BLOCKING_RECV_TIMEOUT_10MS)) {
1563 if (isClient) {
1564 blockingRecvTimeout = DEFAULT_BLOCKING_RECV_TIMEOUT_SECS_CLIENT;
1565 }
1566 else {
1567 blockingRecvTimeout = DEFAULT_BLOCKING_RECV_TIMEOUT_SECS_SERVER;
1568 }
1569 }
1570 }
1571 else {
1572 /* not blocking: set the timeout to return immediately */
1573 blockingRecvTimeout = BLOCKING_RECV_TIMEOUT_NOWAIT;
1574 }
1575
1576 #ifdef VXWORKS /* UDP is not supported on VxWorks */
1577 if (protocol == UDP_PROTOCOL) {
1578 result = RTIOSTREAM_ERROR;
1579 return result;
1580 }
1581 #endif
1582
1583 #ifdef _WIN32
1584 {
1585 WSADATA data;
1586 if (WSAStartup((MAKEWORD(1,1)), &data)) {
1587 result = RTIOSTREAM_ERROR;
1588 printf("WSAStartup() call failed.\n");
1589 }
1590 }
1591 #endif
1592
1593 if (result != RTIOSTREAM_ERROR) {
1594 if (isClient == 1) {
1595 #if (!defined(VXWORKS)) /* Client side connection not supported on VxWorks */
1596 sock = clientOpenSocket(xHostName, xPortNum, protocol);
1597 if (sock == INVALID_SOCKET) {
1598 result = RTIOSTREAM_ERROR;
1599 }
1600 #endif
1601 } else {
1602 sock = serverOpenSocket(xPortNum, serverInfoFile, protocol);
1603 if (sock == INVALID_SOCKET) {
1604 result = RTIOSTREAM_ERROR;
1605 }
1606 }
1607 }
1608
1609 if (result != RTIOSTREAM_ERROR) {
1610 int isServer;
1611 if (isClient == 1) {
1612 isServer = 0;
1613 }
1614 else {
1615 isServer = 1;
1616 }
1617 result = initConnectionData(streamID,
1618 isServer,
1619 protocol,
1620 sock,
1621 blockingRecvTimeout,
1622 maxPacketSize,
1623 xPortNum,
1624 serverInfoFile,
1625 isVerbose,
1626 isUsingSeqNum);
1627 }
1628
1629 if (result != RTIOSTREAM_ERROR) {
1630 result = streamID;
1631 }
1632 else {
1633 if (sock != INVALID_SOCKET) {
1634 /* cleanup */
1635 close(sock);
1636 }
1637 }
1638 return result;
1639 }
1640
1641 /* Function: rtIOStreamSend =====================================================
1642 * Abstract:
1643 * Sends the specified number of bytes on the comm line. Returns the number of
1644 * bytes sent (if successful) or a negative value if an error occurred. As long
1645 * as an error does not occur, this function is guaranteed to set the requested
1646 * number of bytes; the function blocks if tcpip's send buffer doesn't have
1647 * room for all of the data to be sent
1648 */
1649 int rtIOStreamSend(
1650 int streamID,
1651 const void *src,
1652 size_t size,
1653 size_t *sizeSent)
1654 {
1655 int retVal = RTIOSTREAM_NO_ERROR;
1656 ConnectionData * connection = getConnectionData(streamID);
1657 *sizeSent = 0;
1658
1659 if (connection == NULL) {
1660 retVal = RTIOSTREAM_ERROR;
1661 return retVal;
1662 }
1663
1664 if (connection->isServer) {
1665 if (connection->sock == INVALID_SOCKET) {
1666 serverAcceptSocket(connection);
1667 }
1668
1669 if (connection->sock != INVALID_SOCKET) {
1670 #ifndef VXWORKS
1671 retVal = socketDataSet(connection, src, size, sizeSent);
1672 #else
1673 /*
1674 * VXWORKS send prototype does not have src as const. This suppresses
1675 * the compiler warning.
1676 */
1677
1678 retVal = socketDataSet(connection, (char *)src, size, sizeSent);
1679 #endif
1680 }
1681 } else { /* Client stream */
1682 retVal = socketDataSet(connection, src, size, sizeSent);
1683 }
1684
1685 if (connection->isVerbose) {
1686 printf("rtIOStreamSend (connection id %d): size = %lu, sizeSent = %lu\n",
1687 streamID,
1688 (unsigned long) size,
1689 (unsigned long) *sizeSent);
1690 }
1691
1692 return retVal;
1693 }
1694
1695
1696 /* Function: rtIOStreamRecv ================================================
1697 * Abstract: receive data
1698 *
1699 */
1700 int rtIOStreamRecv(
1701 int streamID,
1702 void * dst,
1703 size_t size,
1704 size_t * sizeRecvd)
1705 {
1706 int retVal = RTIOSTREAM_NO_ERROR;
1707 ConnectionData * connection = getConnectionData(streamID);
1708
1709 *sizeRecvd = 0;
1710
1711 if (connection == NULL) {
1712 retVal = RTIOSTREAM_ERROR;
1713 return retVal;
1714 }
1715
1716 if (connection->isServer) {
1717 retVal = serverStreamRecv(connection, dst, size, sizeRecvd);
1718 } else { /* Client stream */
1719 int pending;
1720 if (connection->blockingRecvTimeout != BLOCKING_RECV_TIMEOUT_NEVER) {
1721 /* only call costly "select" if necessary */
1722 retVal = socketDataPending(connection->sock,
1723 connection,
1724 &pending,
1725 connection->blockingRecvTimeout);
1726 }
1727 else {
1728 /* block in "recv" if necessary */
1729 pending = 1;
1730 }
1731 if (pending) {
1732 retVal = socketDataGet(connection, (char *)dst, size, sizeRecvd);
1733 }
1734 }
1735
1736 if (connection->isVerbose) {
1737 printf("rtIOStreamRecv (connection id %d): size = %lu, sizeRecvd = %lu\n",
1738 streamID, (unsigned long) size, (unsigned long) *sizeRecvd);
1739 }
1740
1741 return retVal;
1742 }
1743
1744 /* Function: rtIOStreamClose ================================================
1745 * Abstract: close the connection.
1746 *
1747 */
1748 int rtIOStreamClose(int streamID)
1749 {
1750 int retVal = RTIOSTREAM_NO_ERROR;
1751 ConnectionData * connection = getConnectionData(streamID);
1752 if (connection == NULL) {
1753 retVal = RTIOSTREAM_ERROR;
1754 return retVal;
1755 }
1756
1757 if (connection->isVerbose) {
1758 printf("rtIOStreamClose (connection id %d)\n", streamID);
1759 }
1760
1761 if (connection->isServer) {
1762 /* Only if the client actually made a connection */
1763 if (connection->sock != INVALID_SOCKET) {
1764 if (connection->protocol == TCP_PROTOCOL) {
1765 /* graceful shutdown */
1766 retVal = waitForClientClose(connection);
1767 }
1768
1769 /* close the socket */
1770 close(connection->sock);
1771 connection->sock = INVALID_SOCKET;
1772 }
1773 if (connection->protocol == TCP_PROTOCOL) {
1774 /* TCP: additionally close the listening socket
1775 *
1776 * for UDP, sock and listenSock are the same
1777 * socket - avoid closing it twice */
1778 close(connection->serverData->listenSock);
1779 }
1780 /* set to INVALID_SOCKET for all protocol types */
1781 connection->serverData->listenSock = INVALID_SOCKET;
1782 } else {
1783 SOCKET cSock = connection->sock;
1784 close(cSock);
1785
1786 }
1787 freeConnectionData(connection);
1788 return retVal;
1789 }
1790
1791
|